package shira.sort;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.junit.Before;
import org.junit.Test;

import shira.exception.sort.SortingException;
import shira.person.Educative;
import shira.person.Ken;
import shira.person.Madrich;
import shira.person.Person.Gender;
import shira.seminar.Camp;
import shira.seminar.Hug;
import static org.mockito.Mockito.*;

/**
 * Tests for the {@link Sorter} class. Those are not a unit test per se, because
 * they use the hug's score calculation...
 * 
 * @author avian
 */
public class SorterTest {

	/* --- Test Methods --- */

	/**
	 * Setup for the tests.
	 */
	@Before
	public void setUp() {
		testedSorter = new Sorter();

	}

	/**
	 * A dumb sort test - tests the sorting of one educative.
	 * 
	 * @throws SortingException
	 */
	@Test
	public void testDumbSort() throws SortingException {
		List<Educative> educatives = generateRandomEducatives(1);
		List<Hug> hugs = generateEmptyHugs(1);
		SortingScore sortingScore = new SortingScore(1, 1, 1, 1, 1, 1);
		testedSorter.sort(sortingScore, educatives, hugs);
		Hug hug = hugs.get(0);
		assertEquals(1, hug.getEducativesCount());
		assertEquals(educatives.get(0), hug.getEducatives().get(0));
	}

	/**
	 * Tests that an educative is not thrown into a hug which has a madrich from
	 * the same ken in it.
	 * 
	 * @throws SortingException
	 */
	@Test
	public void testSameKenViolation() throws SortingException {
		List<Educative> educatives = generateRandomEducatives(1);
		Ken ken = new Ken();
		educatives.get(0).setKen(ken);
		Madrich madrich = mock(Madrich.class);
		when(madrich.getKen()).thenReturn(ken);
		when(madrich.isAllowSameKen()).thenReturn(false);
		List<Hug> hugs = generateEmptyHugs(2);
		hugs.get(0).addMadrich(madrich);
		SortingScore sortingScore = new SortingScore(1, 1, 1, 1, 1, 1);
		testedSorter.sort(sortingScore, educatives, hugs);
		assertEquals(0, hugs.get(0).getEducativesCount());
		assertEquals(1, hugs.get(1).getEducativesCount());
		verify(madrich).getKen();
		verify(madrich).isAllowSameKen();
	}

	/**
	 * Not an actual test but used to stress test the sorting. Sorts 1000
	 * educatives into 30 hugs.
	 * 
	 * @throws SortingException
	 */
	@Test
	public void testStressTest() throws SortingException {
		List<Educative> educatives = generateRandomEducatives(1000);
		List<Hug> hugs = generateEmptyHugs(30);
		SortingScore sortingScore = new SortingScore(100, 100, 100, 100, 100, 100);
		testedSorter.sort(sortingScore, educatives, hugs);
		for (Educative educative : educatives) {
			assertNotNull(educative.getHug());
		}
	}

	/* --- Private Methods --- */

	/**
	 * Generates a list of random educatives.
	 * 
	 * @param numOfEducatives
	 *            The number of educatives to generate.
	 * @return The list of the generated educatives.
	 */
	private List<Educative> generateRandomEducatives(int numOfEducatives) {
		List<Educative> educatives = new ArrayList<Educative>();
		Random random = new Random();
		for (int i = 0; i < numOfEducatives; i++) {
			Educative educative = new Educative();
			educative.setName("Random educative #" + i);
			int genderRandom = random.nextInt(2);
			if (genderRandom == 0) {
				educative.setGender(Gender.MALE);
			} else {
				educative.setGender(Gender.FEMALE);
			}
			educative.setKen(new Ken());
			educatives.add(educative);
		}
		return educatives;
	}

	/**
	 * Generate a list of empty hugs.
	 * 
	 * @param numOfHugs
	 *            The number of hugs to generate.
	 * @return The list of generated hugs
	 */
	private List<Hug> generateEmptyHugs(int numOfHugs) {
		Camp funCamp = new Camp("Fun camp");
		List<Hug> hugs = new ArrayList<Hug>();
		for (int i = 0; i < numOfHugs; i++) {
			Hug hug = new Hug();
			hug.setHugNum(i);
			hug.setCamp(funCamp);
			hugs.add(hug);
		}
		return hugs;
	}

	/* --- Data Members --- */

	/** The Sorter under test. */
	Sorter testedSorter;

}
